<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      //创建检查者类
      class Watcher {
        //name指监视的属性名,target指实例对象 callback执行的回调
        constructor(target, name, callback) {
          this.callback = callback;
          this.target = target;
          this.name = name;
          //保证dep能得到watcher
          Dep.target = this;
          this.get();
          Dep.target = null;
        }

        //触发依赖
        get() {
          return this.target[this.name];
        }

        //update跟新方法
        update(newValue) {
          this.callback(newValue);
        }
      }

      //创造依赖储存处dep
      class Dep {
        constructor() {
          this.subs = [];
        }

        //添加观察者方法
        addSubs(sub) {
          this.subs.push(sub);
        }

        //通知观察者
        inform(newValue) {
          this.subs.forEach((watcher) => {
            // console.log(watcher);
            watcher.update(newValue);
          });
        }
      }

      //数据劫持函数
      function definHijack(data, key, value) {
        let dep = new Dep();
        //对每一个数据进行数据劫持
        Object.defineProperty(data, key, {
          get() {
            console.log("妄图读取");
            //深度数据劫持
            if (typeof value === "object") {
              obsever(value);
            }
            //添加观察者
            Dep.target && dep.addSubs(Dep.target);

            return value;
          },
          set(newvalue) {
            console.log("妄图改变");
            //通知观察者触发依赖
            dep.inform(newvalue);
            value = newvalue;
          },
        });
      }

      function obsever(data) {
        if (typeof data !== "object" || data === null) {
          return;
        }
        for (const key in data) {
          definHijack(data, key, data[key]);
        }
      }

      class Vue {
        constructor(config) {
          this.data = config.data;

          //数据代理 使config里的数据都代理到vue实例身上

          for (const key in this.data) {
            Object.defineProperty(this, key, {
              get() {
                return this.data[key];
              },
              set(newvalue) {
                this.data[key] = newvalue;
              },
            });
          }

          //对data进行数据劫持
          obsever(this.data);
        }
      }

      const vm = new Vue({
        data: {
          name: 12,
          map: {
            name: "中国",
            childe: {
              space: "武汉",
            },
          },
        },
      });
      function add() {
        new Watcher(vm, "name", (value) => {
          console.log(value);
        });
        vm.name++;
      }
      add();
      console.log(vm);
      console.log(vm.name);
    </script>
  </body>
</html>
